﻿/*
 * Created by SharpDevelop.
 * Date: 23.10.2011 г.
 * Time: 13:49 ч.
 * 
 */
using System;
using System.IO;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Xml.Serialization; // to resolve the [XmlIgnore] attribute

namespace CaesarCoder
{
	/// <summary>
	/// ConversionTable holds information about a table used in encrypting a file.
	/// </summary>
	public class ConversionTable
	{
		//default constructor -- required to deserialize the Object
		public ConversionTable()
		{
			this.Name = "";
			this.FilePath = "";
			this.Description = "";
			this.Table = new Dictionary<byte, byte>();
			this.ErrorText = "";
		}
		
		/// <summary>
		/// Copy constructor. Creates a copy of the given ConversionTable
		/// </summary>
		/// <param name="ctable">The ConvesionTable object to copy</param>
		public ConversionTable(ConversionTable ctable)
		{
			if (ctable == null)
			{
				throw new ArgumentNullException("ctable", "The parameter cannot be null");
			}
			else
			{
				this.Name = ctable.Name;
				this.Description = ctable.Description;
				this.FilePath = ctable.FilePath;
				if (ctable.ErrorText != null) this.ErrorText = ctable.ErrorText;
				if (ctable.Table.Count > 0)
				{
					this.Table = new Dictionary<byte, byte>();
					foreach (KeyValuePair<byte, byte> elem in ctable.Table)
					{
						this.Table.Add(elem.Key, elem.Value);
					}
				}
				else
				{
					this.Table = new Dictionary<byte, byte>();
				}
			}
		}
		
		/// <param name="jsonFilePath">Path to a JSON-encoded file containing the conversion table</param>
		public ConversionTable(string jsonFilePath)
		{
			//initialize properties
			this.Name = "";
			this.Description = "";
			this.ErrorText = "";
			if ( (jsonFilePath != null) && (jsonFilePath.Length > 0) && (File.Exists(jsonFilePath)) )
			{
				this.FilePath = jsonFilePath;
				this.Table = new Dictionary<byte, byte>();
				StreamReader sr = new StreamReader(jsonFilePath);
				if (sr != null)
				{
					using(sr)
					{
						if(sr.BaseStream.Length > 0)
						{
							string jsonText = sr.ReadToEnd();
							sr.Close();
							ConversionTable tempCT = (ConversionTable)fastJSON.JSON.Instance.ToObject(jsonText, typeof(ConversionTable));
							if (tempCT.isValid())
							{
								this.Name = tempCT.Name;
								this.Description = tempCT.Description;
								this.Table = tempCT.Table;
								this.FilePath = jsonFilePath;
							}
						}
					}
				}
			} //if
			else
			{
				this.FilePath = "";
			}
		}
		
		/// <param name="TableName">The name of the Conversion Table</param>
		/// <param name="TableDescription">The description of the Conversion Table</param>
		public ConversionTable(string TableName, string TableDescription)
		{
			//initialize properties
			if (TableName != null) this.Name = TableName; else this.Name = "";
			if (TableDescription != null) this.Description = TableDescription; else this.Description = "";
			this.Table = new Dictionary<byte, byte>();
			this.ErrorText = "";
		}
		
		public void Reload()
		{
			this.Name = "";
			this.Description = "";
			this.ErrorText = "";
			this.Table = new Dictionary<byte, byte>();
			if (File.Exists(this.FilePath))
			{
				StreamReader sr = new StreamReader(this.FilePath);
				if (sr != null)
				{
					using(sr)
					{
						if(sr.BaseStream.Length > 0)
						{
							string jsonText = sr.ReadToEnd();
							sr.Close();
							ConversionTable tempCT = (ConversionTable)fastJSON.JSON.Instance.ToObject(jsonText, typeof(ConversionTable));
							if (tempCT.isValid())
							{
								this.Name = tempCT.Name;
								this.Description = tempCT.Description;
								this.Table = tempCT.Table;
							}
						}
					}
				}
			} //if (File.Exists(...)
			else
			{
				this.FilePath = "";
			}
		}
		
		[XmlIgnore]
		private string FilePath {get; set;}
		
		public string Name {get; set;}
		
		public string Description {get; set;}
		
		public Dictionary<byte, byte> Table {get; set;}
		
		[XmlIgnore]
		public string ErrorText {get; set;}
		
		/// <summary>
		/// Checks if the ConversionTable object has valid content
		/// </summary>
		/// <returns>true - if the content is valid, false - otherwise</returns>
		public bool isValid()
		{
			this.ErrorText = "";
			bool valid_table = false;
			if ((this.Name.Length > 0) && (this.Description.Length > 0) && (this.Table.Count > 0))
			{
				valid_table = true;
			} else {
				if (this.Name == "") this.ErrorText += "Name cannot be empty\n";
				if (this.Description == "") this.ErrorText += "Description cannot be empty\n";
				if (!(this.Table.Count > 0)) this.ErrorText += "Table should contain at least 1 row";
			}
			if (this.Table.Keys.Count != this.Table.Values.Count)
			{
				valid_table = false;
				this.ErrorText += "There is an empty Value for a Key\n";
			}
			foreach (byte b in this.Table.Keys)
			{
				if (!this.Table.ContainsValue(b))
				{
					if (this.ErrorText.Length == 0) this.ErrorText += "Each Key should also be used as Value\n";
					this.ErrorText += "Key '" + b.ToString() +"' has not been used\n";
				}
			}
			if (this.ErrorText != "") valid_table = false;
			return valid_table;
		}
		
		/// <summary>
		/// Saves the ConversionTable object
		/// </summary>
		/// <returns>true - on success,  false - if something went wrong</returns>
		public bool Save()
		{
			if (this.FilePath == null || this.FilePath.Length == 0) return false;
			
			return SaveAs(this.FilePath);
		}
		
		/// <summary>
		/// Saves the ConversionTable object to a JSON-encoded file
		/// </summary>
		/// <param name="TargetFile">The file to save the object to</param>
		/// <returns>true - on success,  false - if something went wrong</returns>
		public bool SaveAs(string TargetFile)
		{
			if (TargetFile == null || TargetFile.Length == 0) return false;
			this.FilePath = TargetFile;
			
			fastJSON.JSON.Instance.IndentOutput = true; // set to false to save space
			fastJSON.JSON.Instance.SerializeNullValues = false;
			fastJSON.JSON.Instance.UseSerializerExtension = false; // do not write extensions like $type
			
			string jsonText = fastJSON.JSON.Instance.ToJSON(this);
			StreamWriter sw = new StreamWriter(TargetFile, false);
			
			bool success = true;
			try
			{
				using(sw)
				{
					sw.Write(jsonText);
					//sw.Close(); //not necessary if encapsulated in a 'using' statement
				}
			} catch (Exception exsw) {
				success = false;
				System.Windows.Forms.MessageBox.Show("Error: Could not write ConversionTable.\n\nOriginal error: " + exsw.Message);
			} finally {
				if (success) System.Windows.Forms.MessageBox.Show("Success!\nConversionTable has been written to " + TargetFile);
			}
			return success;
		}
		
		/// <summary>
		/// Deletes the ConversionTable physically from disk
		/// </summary>
		public bool DeleteFromDisk()
		{
			if (this.FilePath != null && this.FilePath != "" && File.Exists(this.FilePath))
			{
				File.Delete(this.FilePath);
				return true;
			}
			return false;
		}
		
		/// <summary>
		/// Encrypts a file and saves it to 'TargetPath'
		/// </summary>
		/// <param name="SourcePath"></param>
		/// <param name="TargetPath"></param>
		public bool EncryptFile(string SourcePath, string TargetPath)
		{
			if (SourcePath == null || TargetPath == null || !File.Exists(SourcePath)) return false;
			
			byte[] SourceContent; //byte array which holds the data from the Source File
			byte[] TargetContent; //byte array which holds the data from the Target File
			bool Success = true;
			StreamReader sr = null;
			try
			{
				if ((sr = new StreamReader(SourcePath)) != null) {
					using (sr)
					{
						if(sr.BaseStream.Length == 0)
						{
							return false;
						}
						else
						{
							int len32 = Convert.ToInt32(sr.BaseStream.Length);
							SourceContent = new byte[len32];
							if (sr.BaseStream.Read(SourceContent, 0, len32) == sr.BaseStream.Length)
							{
								// close the StreamReader so that we can write to this file if needed
								sr.Close();
								TargetContent = new byte[len32];
								
								// do some manipulations to the source file and save them in TargetContent
								byte RetValue;
								for(int i=0; i<len32; i++)
								{
									if(this.Table.TryGetValue(SourceContent[i], out RetValue))
									{
										TargetContent[i] = RetValue;
									}
									else
									{
										TargetContent[i] = SourceContent[i];
									}
								}
								
								// finally write the modified bytes to "Target file"
								StreamWriter sw = new StreamWriter(TargetPath);
								try
								{
									using(sw)
									{
										sw.BaseStream.Write(TargetContent, 0, len32);
										//sw.Close(); //not necessary if encapsulated in a 'using' statement
									}
								} catch (Exception /*ex2*/) {
									Success = false;
									//MessageBox.Show("Error: Could not write all bytes to '" + TargetPath + "'.\n\nOriginal error: " + ex2.Message);
								} finally {
									//if (Success == true) MessageBox.Show("Success! All bytes have been written to '" + TargetPath + "'");
								}
								return Success;
							}
							else
							{
								return false;
							}
						}
					}//if ((sr = new StreamReader
				}//try
			}
			catch (Exception ex)
			{
				Success = false;
				MessageBox.Show("Error: Could not read file from disk. Original error: " + ex.Message);
			}
			return Success;
		}
		
		public bool DecryptFile(string EncryptedFilePath, string TargetPath)
		{
			if (EncryptedFilePath == null || TargetPath == null || !File.Exists(EncryptedFilePath)) return false;
			
			byte[] SourceContent; //byte array which holds the data from the Source File
			byte[] TargetContent; //byte array which holds the data from the Target File
			bool Success = true;
			StreamReader sr = null;
			try
			{
				if ((sr = new StreamReader(EncryptedFilePath)) != null) {
					using (sr)
					{
						if(sr.BaseStream.Length == 0)
						{
							return false;
						}
						else
						{
							int len32 = Convert.ToInt32(sr.BaseStream.Length);
							SourceContent = new byte[len32];
							if (sr.BaseStream.Read(SourceContent, 0, len32) == sr.BaseStream.Length)
							{
								// close the StreamReader so that we can write to this file if needed
								sr.Close();
								TargetContent = new byte[len32];
								
								// do some manipulations to the source file and save them in TargetContent
								Dictionary<byte, byte> RevDicTable = new Dictionary<byte, byte>();
								foreach (KeyValuePair<byte, byte> pair in this.Table)
								{
									RevDicTable.Add(pair.Value, pair.Key);
								}

								byte RetValue;
								for(int i=0; i<len32; i++)
								{
									if(RevDicTable.TryGetValue(SourceContent[i], out RetValue))
									{
										TargetContent[i] = RetValue;
									}
									else
									{
										TargetContent[i] = SourceContent[i];
									}
								}
								
								// finally write the modified bytes to "Target file"
								StreamWriter sw = new StreamWriter(TargetPath);
								try
								{
									using(sw)
									{
										sw.BaseStream.Write(TargetContent, 0, len32);
										//sw.Close(); //not necessary if encapsulated in a 'using' statement
									}
								} catch (Exception /*ex2*/) {
									Success = false;
									//MessageBox.Show("Error: Could not write all bytes to '" + TargetPath + "'.\n\nOriginal error: " + ex2.Message);
								} finally {
									//if (Success == true) MessageBox.Show("Success! All bytes have been written to '" + TargetPath + "'");
								}
								return Success;
							}
							else
							{
								return false;
							}
						}
					}//if ((sr = new StreamReader
				}//try
			}
			catch (Exception ex)
			{
				Success = false;
				MessageBox.Show("Error: Could not read file from disk. Original error: " + ex.Message);
			}
			return Success;
		}
	}
}
